import java.io.*;
import java.math.BigInteger;
import java.net.*;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class Client implements Runnable{
    
    // Declaration section
    // clientClient: the client socket
    // os: the output stream
    // is: the input stream
    
    static Socket clientSocket = null;
    static PrintStream os = null;
    static DataInputStream is = null;
    static DataOutputStream dOS = null;
    static BufferedReader inputLine = null;
    static boolean closed = false;
    static Encryption Aencrypter;
    
    public static void main(String[] args) {
    	  BigInteger[] pg = null;
    	  byte[] apub = null;
  		  String apubstr = "";
  		byte[] bpub = null;
  		String bpubstr = null;
    	  DiffieHellman dha = new DiffieHellman();
	// The default port	
	int port_number=2323;
        String host="localhost";
	
	if (args.length < 2)
	    {
		System.out.println("Usage: java MultiThreadChatClient  \n"+
				   "Now using host="+host+", port_number="+port_number);
	    } else {
		host=args[0];
		port_number=Integer.valueOf(args[1]).intValue();
	    }
	// Initialization section:
	// Try to open a socket on a given host and port
	// Try to open input and output streams
	try {
            clientSocket = new Socket(host, port_number);
            inputLine = new BufferedReader(new InputStreamReader(System.in));
            os = new PrintStream(clientSocket.getOutputStream());
            is = new DataInputStream(clientSocket.getInputStream());
            dOS = new DataOutputStream(clientSocket.getOutputStream());
            
            pg = dha.GeneratePG();
            apub  = dha.CreateSendInteger(pg[0], pg[1], 16);
            apubstr = apub.toString();
            
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host "+host);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to the host "+host);
        }
	
	// If everything has been initialized then we want to write some data
	// to the socket we have opened a connection to on port port_number 
	
        if (clientSocket != null && os != null && is != null) {
            try {
		
		// Create a thread to read from the server

            	synchronized (clientSocket) {
            		dOS.writeBytes(String.valueOf(pg[0]) + "\n");
            		//System.out.println("Got pg0");
            	}
            	synchronized (clientSocket) {
            		dOS.writeBytes(String.valueOf(pg[1]) + "\n");
            		//System.out.println("Got pg1");
            	}

            	synchronized (clientSocket) {
            		dOS.writeBytes(String.valueOf(apub.length) + "\n");
            		//System.out.println("Did apub");
            	}
                System.out.println("Byte Length: " + apub.length);
                try {
					Thread.sleep(1000l);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
                synchronized (clientSocket) {  
                	//System.out.println("Sending apub");
                dOS.write(apub, 0, apub.length);
                
                //System.out.println("Done sending apub");
                
                }
//                System.out.println("Done with write");

               
                
                
                
                //System.out.println("P: "+ pg[0] + "\ng: " + pg[1]);
                //bpubstr = is.readLine();
				//while(!inputLine.ready()) {System.out.println("waiting.");}
				//System.out.println(is.readLine());
                int bytsToRead = 0;
				synchronized (clientSocket) {
                bytsToRead = is.readInt(); 
                	//Integer.parseInt(is.readLine());
				}
                
//                System.out.println("Byte Length Recieved " + bytsToRead );
                bpub = new byte[bytsToRead];
                synchronized(clientSocket) {
                
               is.read(bpub, 0, bpub.length);
                //is.readFully(bpub);
  
                
                
				}
//				for(byte b : apub) {
//    				System.out.print(b + ", ");
//    			}
                //System.out.println("Apubstr: " + apubstr + " BPubstr: " + bpubstr);
//                System.out.println("Apub: "+ apub + "\nBpub: " + bpub);
    		    SecretKey Ka = dha.CreateFullKey(bpub);
//    		    System.out.println("Secret Key: " + Ka);
    		    Aencrypter = new Encryption(Ka);
    		    
                new Thread(new Client()).start();

                synchronized (clientSocket) {
					
				
                	while (!closed) {
                		
	                    os.println(Aencrypter.encrypt(inputLine.readLine())); 
	                }
	            }
		// Clean up:
		// close the output stream
		// close the input stream
		// close the socket
		
		os.close();
		is.close();
		clientSocket.close();   
            } catch (IOException e) {
                System.err.println("IOException:  " + e);
            }
        }
    }           
    
    public void run() {		
	String responseLine;
	
	// Keep on reading from the socket till we receive the "Bye" from the server,
	// once we received that then we want to break.
	try{ 
	    while (true) {
	    	//String temp = is.readLine();
	    	//responseLine = Aencrypter.decrypt(temp);
	    responseLine = EncComm.recieve(is, Aencrypter);
		System.out.println(responseLine);
		//System.out.println("Encrypted: " + temp);
		if (responseLine.indexOf("*** Bye") != -1) break;
	    }
            closed=true;
	} catch (Exception e) {
	    System.err.println("IOException:  " + e);
	}
    }
}